﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

using System.Globalization;

namespace Neto.Extensions;

/// <summary>
///     实体扩展
/// </summary>
public static class EntityExtensions
{
    /// <summary>
    ///     获取class指定字段的描述（ Description）
    /// </summary>
    /// <param name="obj">class对象</param>
    /// <param name="fieldName">字段名: 使用 nameof(字段名)</param>
    /// <returns>字段的描述字符串</returns>
    public static string GetClassFieldDescription(this object obj, string fieldName)
    {
        var attributes = TypeDescriptor.GetProperties(obj)[fieldName]?.Attributes;
        var attribute = (DescriptionAttribute)attributes?[typeof(DescriptionAttribute)]!;

        return attribute.Description;
    }

    /// <summary>
    ///     通过枚举值 获取枚举的 描述（Description）
    /// </summary>
    /// <param name="enumValue">枚举值</param>
    /// <returns>枚举的描述字符串</returns>
    public static string GetDescriptionByEnum(this Enum enumValue)
    {
        var value = enumValue.ToString();
        var field = enumValue.GetType().GetField(value);
        var objs = field?.GetCustomAttributes(typeof(DescriptionAttribute), false); //获取描述属性
        if (objs is { Length: 0 }) //当描述属性没有时，直接返回名称
            return value;
        var descriptionAttribute = (DescriptionAttribute)objs?[0]!;
        return descriptionAttribute.Description;
    }

    // 调用示例
    //GetDescriptionByEnum(enumStudent.age) → 年龄

    /// <summary>
    ///     通过描述（Description）获取枚举值
    /// </summary>
    /// <typeparam name="T">枚举类型</typeparam>
    /// <param name="description">枚举值描述</param>
    /// <returns>枚举值</returns>
    /// <exception cref="ArgumentException">当描述未找到对应的枚举时抛出</exception>
    public static T? GetEnumByDescription<T>(this string description) where T : Enum
    {
        var fields = typeof(T).GetFields();
        foreach (var field in fields)
        {
            var objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false); //获取描述属性
            if (objs.Length > 0 && (objs[0] as DescriptionAttribute)?.Description == description)
                return (T)field.GetValue(null)!;
        }

        throw new ArgumentException($"{description} 未能找到对应的枚举.", "Description");
    }

    /// <summary>
    ///     获取class\Enum 的描述（ Description）
    /// </summary>
    /// <param name="obj">class\Enum对象</param>
    /// <returns>对象的描述字符串</returns>
    public static string GetDescription(this object? obj)
    {
        if (obj == null) return "";
        // 获取类的 Type
        var exampleType = obj.GetType();

        // 使用反射获取 Description 属性
        var attributes = exampleType.GetCustomAttributes(typeof(DescriptionAttribute), false);

        // 检查属性数组是否为空
        if (attributes.Length <= 0) return "";
        // 确保类型正确
        if (attributes[0] is DescriptionAttribute descriptionAttribute) return descriptionAttribute.Description;

        // 如果没有找到 DescriptionAttribute 或者其他异常情况，返回空字符串
        return "";
    }


    #region 数据库扩展字段，存储 Dictionary<string,T>  的操作

    /// <summary>
    ///     获取指定Key的数据
    /// </summary>
    /// <param name="data">JSON格式的字符串</param>
    /// <param name="key">键名</param>
    /// <param name="defaultValue">默认值</param>
    /// <typeparam name="T">值的类型</typeparam>
    /// <returns>指定键对应的值，如果键不存在则返回默认值</returns>
    public static T? GetValue<T>(this string data, string key, T? defaultValue = default)
    {
        if (string.IsNullOrEmpty(key)) throw new ArgumentException($"“{nameof(key)}”不能为 null 或空。", nameof(key));
        try
        {
            var dic = data.ToObj2<Dictionary<string, object>>();
            if (dic == null) return defaultValue;

            var state = dic.TryGetValue(key, out var value);
            if (state)
                return value.To<T>();
            return defaultValue;
        }
        catch (Exception)
        {
            return default;
        }
    }

    /// <summary>
    ///     获取指定Key的数据
    /// </summary>
    /// <param name="data">JSON格式的字符串</param>
    /// <param name="key">键名</param>
    /// <returns>指定键对应的字符串值，如果键不存在则返回null</returns>
    public static string? GetValue(this string data, string key)
    {
        return GetValue<string>(data, key);
    }

    /// <summary>
    ///     获取指定Key的数据
    /// </summary>
    /// <param name="data">JSON格式的字符串</param>
    /// <param name="key">键名</param>
    /// <typeparam name="T">值的类型</typeparam>
    /// <returns>指定键对应的对象，如果键不存在则返回null</returns>
    public static T? GetObject<T>(this string? data, string key)
    {
        try
        {
            if (data == null) return default;
            if (string.IsNullOrWhiteSpace(data)) return default;
            var dic = data.ToObj2<Dictionary<string, string>>();
            if (dic == null) return default;

            var state = dic.TryGetValue(key, out var value);
            if (state && value != null)
                return value.ToObj<T>();
            return default;
        }
        catch (Exception)
        {
            return default;
        }
    }

    /// <summary>
    ///     获取指定Key的数据
    /// </summary>
    /// <param name="data">JSON格式的字符串</param>
    /// <param name="key">键名</param>
    /// <returns>指定键对应的字符串对象，如果键不存在则返回null</returns>
    public static string? GetObject(this string? data, string key)
    {
        return GetObject<string>(data, key);
    }

    /// <summary>
    ///     设置数据到 集合中去
    /// </summary>
    /// <param name="data">JSON格式的字符串</param>
    /// <param name="key">键名</param>
    /// <param name="value">键对应的值</param>
    /// <returns>更新后的JSON字符串</returns>
    public static string? SetData(this string data, string key, string value)
    {
        var dic = (data ?? "{}").ToObj2<Dictionary<string, string>>();
        if (dic == null) dic = new Dictionary<string, string>();
        if (dic.ContainsKey(key))
            dic[key] = value;
        else
            dic.Add(key, value);
        return dic.ToJson();
    }

    #endregion 数据库扩展字段，存储 Dictionary<string,T>  的操作

    #region 对象属性扩展

    /// <summary>
    ///     设置对象属性的值（支持基本类型自动转换）
    /// </summary>
    /// <param name="obj">目标对象</param>
    /// <param name="propName">属性名称</param>
    /// <param name="value">要设置的值</param>
    public static void FieldSetValue(this object obj, string propName, object value)
    {
        ArgumentNullException.ThrowIfNull(obj);
        if (string.IsNullOrEmpty(propName)) throw new ArgumentException("属性名不能为空", nameof(propName));

        var type = obj.GetType();
        var property = type.GetProperty(propName);

        if (property == null || !property.CanWrite) return;

        try
        {
            if (property.PropertyType.IsInstanceOfType(value))
                property.SetValue(obj, value, null);
            else
            {
                var convertedValue = Convert.ChangeType(value, property.PropertyType, CultureInfo.InvariantCulture);
                property.SetValue(obj, convertedValue, null);
            }
        }
        catch (Exception ex) when (ex is TargetParameterCountException or TargetInvocationException)
        {
            // 可记录日志
        }
    }


    /// <summary>
    ///     获取指定字段的值
    /// </summary>
    /// <typeparam name="TValue">期望返回的值类型</typeparam>
    /// <param name="obj">对象实例</param>
    /// <param name="fieldName">字段名</param>
    /// <returns>字段值，如果找不到或转换失败则返回默认值</returns>
    public static TValue? GetFieldValue<TValue>(this object obj, string fieldName) where TValue : class
    {
        ArgumentNullException.ThrowIfNull(obj);
        if (string.IsNullOrEmpty(fieldName)) throw new ArgumentException("字段名不能为空", nameof(fieldName));

        var property = obj.GetType().GetProperty(fieldName);
        if (property == null) return null;

        try
        {
            var value = property.GetValue(obj, null);
            return value as TValue;
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    ///     获取指定字段的值
    /// </summary>
    /// <typeparam name="TValue">期望返回的值类型</typeparam>
    /// <param name="obj">对象实例</param>
    /// <param name="fieldName">字段名</param>
    /// <returns>字段值，如果找不到或转换失败则返回默认值</returns>
    public static TValue? GetFieldValueByValueType<TValue>(this object obj, string fieldName) where TValue : struct
    {
        ArgumentNullException.ThrowIfNull(obj);
        if (string.IsNullOrEmpty(fieldName)) throw new ArgumentException("字段名不能为空", nameof(fieldName));

        var property = obj.GetType().GetProperty(fieldName);
        if (property == null) return null;

        try
        {
            var value = property.GetValue(obj, null);
            if (value == null) return null;

            return (TValue)Convert.ChangeType(value, typeof(TValue), CultureInfo.InvariantCulture);
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    ///     获取对象的属性信息及其描述
    /// </summary>
    /// <param name="obj">对象实例</param>
    /// <returns>包含字段名、值、描述的字典</returns>
    public static Dictionary<string, NetoEntityInfo> GetDictionaryEntityInfo(this object obj)
    {
        if (obj == null) throw new ArgumentNullException(nameof(obj));

        var result = new Dictionary<string, NetoEntityInfo>();
        foreach (var prop in obj.GetType().GetProperties())
        {
            var value = prop.GetValue(obj)?.ToString() ?? "";
            var description = prop.GetDescription();

            result[prop.Name] = new NetoEntityInfo { Filed = prop.Name, Value = value, Description = description };
        }

        return result;
    }

    /// <summary>
    ///     用于获取属性描述
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    private static string GetDescription(this PropertyInfo property)
    {
        var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(
            property,
            typeof(DescriptionAttribute),
            false)!;

        return attribute?.Description ?? string.Empty;
    }

    /// <summary>
    ///     获取对象属性名与值的字典
    /// </summary>
    /// <typeparam name="TValue">值的类型</typeparam>
    /// <param name="obj">对象实例</param>
    /// <returns>属性名/值字典</returns>
    public static Dictionary<string, TValue?> GetDictionary<TValue>(this object? obj)
    {
        if (obj == null) return new Dictionary<string, TValue?>();

        var result = new Dictionary<string, TValue?>();
        foreach (var prop in obj.GetType().GetProperties())
            try
            {
                var value = prop.GetValue(obj);
                if (value is Enum @enum)
                    result[prop.Name] = (TValue)(object)@enum.GetHashCode();
                else if (value is TValue value1)
                    result[prop.Name] = value1;
                else
                    result[prop.Name] = value.To<TValue>();
            }
            catch
            {
                // 可选：记录日志
            }

        return result;
    }

    /// <summary>
    ///     获取按字母顺序排序的属性名/值字典
    /// </summary>
    /// <param name="obj">对象实例</param>
    /// <returns>排序后的字典</returns>
    public static SortedDictionary<string, string> GetSortedDictionary(this object obj)
    {
        var dict = obj.GetDictionary<string>();
        return new SortedDictionary<string, string>(dict);
    }

    /// <summary>
    ///     获取对象属性名/字符串值的字典
    /// </summary>
    /// <param name="obj">对象实例</param>
    /// <returns>属性名/字符串值字典</returns>
    public static Dictionary<string, string> GetDictionary(this object obj)
    {
        return obj.GetDictionary<string>();
    }

    #endregion 对象属性扩展

    // 调用示例
    //GetEnumByDescription<enumStudent>("性别").ToString() → sex

    #region summary 读取

    /// <summary>
    ///     获取类的实例类型。
    /// </summary>
    /// <param name="className">类实例</param>
    /// <typeparam name="TClass">类类型</typeparam>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException">NULL 异常</exception>
    private static Type GetTypeFromInstance<TClass>(TClass className)
    {
        if (className == null) throw new ArgumentNullException(nameof(className));
        return typeof(TClass);
    }

    /// <summary>
    ///     获取类的注释信息。
    /// </summary>
    /// <typeparam name="TClass">类类型</typeparam>
    /// <param name="className">类实例</param>
    /// <returns>类的注释字符串</returns>
    public static string GetClassComment<TClass>(this TClass className)
    {
        var type = GetTypeFromInstance(className);
        return XmlCommentHelper.GetTypeComment(type);
    }

    /// <summary>
    ///     获取指定字段的注释信息。
    /// </summary>
    /// <typeparam name="TClass">类类型</typeparam>
    /// <param name="className">类实例</param>
    /// <param name="fieldName">字段名称</param>
    /// <returns>字段的注释字符串</returns>
    public static string GetFieldComment<TClass>(this TClass className, string fieldName)
    {
        var type = GetTypeFromInstance(className);
        var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);

        return field == null ? string.Empty : XmlCommentHelper.GetFieldOrPropertyComment(field);
    }

    /// <summary>
    ///     获取指定方法的注释信息。
    /// </summary>
    /// <typeparam name="TClass">类类型</typeparam>
    /// <param name="className">类实例</param>
    /// <param name="classMethodName">方法名称</param>
    /// <returns>方法的注释字符串</returns>
    public static string GetClassMethodComment<TClass>(this TClass className, string classMethodName)
    {
        if (string.IsNullOrEmpty(classMethodName))
            throw new ArgumentNullException(nameof(classMethodName));

        var type = GetTypeFromInstance(className);
        var method = type.GetMethod(classMethodName);

        return method == null ? string.Empty : XmlCommentHelper.GetMethodComment(method);
    }

    /// <summary>
    ///     获取指定方法的参数注释字典。
    /// </summary>
    /// <typeparam name="TClass">类类型</typeparam>
    /// <param name="className">类实例</param>
    /// <param name="classMethodName">方法名称</param>
    /// <returns>包含参数名及其注释的字典</returns>
    public static Dictionary<string, string> GetClassMethodPars<TClass>(this TClass className, string classMethodName)
    {
        if (string.IsNullOrEmpty(classMethodName))
            throw new ArgumentNullException(nameof(classMethodName));

        var type = GetTypeFromInstance(className);
        var method = type.GetMethod(classMethodName);

        return method == null ? new Dictionary<string, string>() : XmlCommentHelper.GetParameterComments(method);
    }

    #endregion summary 读取
}

/// <summary>
///     实体信息
/// </summary>
public class NetoEntityInfo
{
    /// <summary>
    ///     字段名称
    /// </summary>
    public string Filed { get; set; }

    /// <summary>
    ///     值
    /// </summary>
    public string Value { get; set; }

    /// <summary>
    ///     名称
    /// </summary>
    public string Description { get; set; }
}